# test.py
import unittest
import tempfile
import os
from datetime import datetime, timedelta
import sys
import tkinter as tk
from tkinter import ttk

# 将主程序目录添加到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

from database import Database
from categories import EXPENSE_CATEGORIES, INCOME_CATEGORIES


class TestDatabase(unittest.TestCase):
    """测试数据库功能"""

    def setUp(self):
        # 创建临时数据库文件用于测试
        self.temp_db = tempfile.NamedTemporaryFile(delete=False, suffix='.db')
        self.temp_db.close()
        self.db = Database(self.temp_db.name)

    def tearDown(self):
        # 清理临时文件
        os.unlink(self.temp_db.name)

    def test_add_transaction(self):
        """测试添加交易记录"""
        # 添加支出记录
        self.db.add_transaction('expense', 100.0, '食品酒水', '餐饮 - 午餐', '公司午餐', '工作日午餐')
        
        # 添加收入记录
        self.db.add_transaction('income', 5000.0, '工资收入', '工资', '月薪', '基本工资')
        
        # 验证记录是否添加成功
        transactions = self.db.get_transactions()
        self.assertEqual(len(transactions), 2)
        
        # 验证第一条记录（支出）
        expense = transactions[0]
        self.assertEqual(expense['type'], 'expense')
        self.assertEqual(expense['amount'], 100.0)
        self.assertEqual(expense['main_category'], '食品酒水')
        self.assertEqual(expense['sub_category'], '餐饮 - 午餐')
        self.assertEqual(expense['item_detail'], '公司午餐')
        self.assertEqual(expense['note'], '工作日午餐')
        
        # 验证第二条记录（收入）
        income = transactions[1]
        self.assertEqual(income['type'], 'income')
        self.assertEqual(income['amount'], 5000.0)

    def test_get_transactions_with_filters(self):
        """测试带过滤条件的交易记录查询"""
        # 添加测试数据
        self.db.add_transaction('expense', 50.0, '食品酒水', '餐饮 - 早餐', '包子豆浆', '')
        self.db.add_transaction('expense', 30.0, '交通出行', '公共交通 - 地铁', '地铁月票', '')
        self.db.add_transaction('income', 8000.0, '工资收入', '工资', '工资', '')
        
        # 测试按类型筛选（修正为正确的参数）
        expenses = self.db.get_transactions(trans_type='expense')
        self.assertEqual(len(expenses), 2)
        self.assertTrue(all(t['type'] == 'expense' for t in expenses))
        
        incomes = self.db.get_transactions(trans_type='income')
        self.assertEqual(len(incomes), 1)
        self.assertTrue(all(t['type'] == 'income' for t in incomes))
        
        # 测试按分类筛选
        food_expenses = self.db.get_transactions(main_category='食品酒水')
        self.assertEqual(len(food_expenses), 1)
        self.assertEqual(food_expenses[0]['main_category'], '食品酒水')

    def test_delete_transaction(self):
        """测试删除交易记录"""
        # 添加测试记录
        self.db.add_transaction('expense', 100.0, '食品酒水', '餐饮 - 午餐', '', '')
        transactions = self.db.get_transactions()
        transaction_id = transactions[0]['id']
        
        # 删除记录
        result = self.db.delete_transaction(transaction_id)
        self.assertTrue(result)
        
        # 验证记录已被删除
        remaining_transactions = self.db.get_transactions()
        self.assertEqual(len(remaining_transactions), 0)

    def test_set_and_get_budget(self):
        """测试预算设置和获取"""
        # 设置预算
        self.db.set_budget('食品酒水', 1000.0, 'monthly')
        self.db.set_budget('交通出行', 200.0, 'monthly')
        
        # 获取预算
        budgets = self.db.get_budgets('monthly')
        self.assertEqual(len(budgets), 2)
        
        # 验证预算值
        food_budget = next((b for b in budgets if b['main_category'] == '食品酒水'), None)
        self.assertIsNotNone(food_budget)
        self.assertEqual(food_budget['amount'], 1000.0)
        
        transport_budget = next((b for b in budgets if b['main_category'] == '交通出行'), None)
        self.assertIsNotNone(transport_budget)
        self.assertEqual(transport_budget['amount'], 200.0)

    def test_get_budget_status(self):
        """测试预算状态获取"""
        # 设置预算
        self.db.set_budget('食品酒水', 500.0, 'monthly')
        
        # 添加支出记录
        self.db.add_transaction('expense', 200.0, '食品酒水', '餐饮 - 午餐', '', '')
        self.db.add_transaction('expense', 150.0, '食品酒水', '餐饮 - 晚餐', '', '')
        
        # 获取预算状态
        now = datetime.now()
        status_list = self.db.get_budget_status(now.year, now.month)
        
        # 查找食品酒水的预算状态
        food_status = next((s for s in status_list if s['category'] == '食品酒水'), None)
        self.assertIsNotNone(food_status)
        self.assertEqual(food_status['budget'], 500.0)
        self.assertEqual(food_status['actual'], 350.0)
        self.assertEqual(food_status['remaining'], 150.0)
        self.assertAlmostEqual(food_status['usage_rate'], 70.0, places=1)  # 350/500 = 70%

    def test_export_to_csv(self):
        """测试导出到CSV"""
        # 添加测试数据
        self.db.add_transaction('expense', 50.0, '食品酒水', '餐饮 - 早餐', '包子豆浆', '')
        self.db.add_transaction('income', 8000.0, '工资收入', '工资', '工资', '')
        
        # 导出到临时CSV文件
        temp_csv = tempfile.NamedTemporaryFile(delete=False, suffix='.csv')
        temp_csv.close()
        
        try:
            result = self.db.export_to_csv(temp_csv.name)
            self.assertTrue(result)
            
            # 检查文件是否存在且不为空
            self.assertTrue(os.path.exists(temp_csv.name))
            self.assertGreater(os.path.getsize(temp_csv.name), 0)
        finally:
            # 清理临时文件
            if os.path.exists(temp_csv.name):
                os.unlink(temp_csv.name)

    def test_get_available_years(self):
        """测试获取可用年份"""
        # 使用新的空数据库文件测试
        temp_db_fresh = tempfile.NamedTemporaryFile(delete=False, suffix='.db')
        temp_db_fresh.close()
        fresh_db = Database(temp_db_fresh.name)
        
        # 新数据库应该没有年份
        years = fresh_db.get_available_years()
        self.assertEqual(len(years), 0)
        
        # 添加记录
        fresh_db.add_transaction('expense', 100.0, '食品酒水', '餐饮', '', '')
        
        # 应该有一个年份
        years = fresh_db.get_available_years()
        self.assertEqual(len(years), 1)
        self.assertEqual(years[0], datetime.now().year)
        
        # 清理
        os.unlink(temp_db_fresh.name)


class TestCategories(unittest.TestCase):
    """测试分类数据"""

    def test_expense_categories_structure(self):
        """测试支出分类结构"""
        # 检查是否有主要分类
        self.assertGreater(len(EXPENSE_CATEGORIES), 0)
        
        # 检查每个主要分类是否都有中类和小类
        for main_cat, middle_cats in EXPENSE_CATEGORIES.items():
            self.assertIsInstance(middle_cats, dict)
            self.assertGreater(len(middle_cats), 0)
            
            for middle_cat, sub_cats in middle_cats.items():
                self.assertIsInstance(sub_cats, list)
                self.assertGreater(len(sub_cats), 0)

    def test_income_categories_structure(self):
        """测试收入分类结构"""
        # 检查是否有主要分类
        self.assertGreater(len(INCOME_CATEGORIES), 0)
        
        # 检查每个主要分类是否都有中类和小类
        for main_cat, middle_cats in INCOME_CATEGORIES.items():
            self.assertIsInstance(middle_cats, dict)
            self.assertGreater(len(middle_cats), 0)
            
            for middle_cat, sub_cats in middle_cats.items():
                self.assertIsInstance(sub_cats, list)
                self.assertGreater(len(sub_cats), 0)


class TestUIComponents(unittest.TestCase):
    """测试UI组件（基础测试）"""

    def setUp(self):
        self.root = tk.Tk()
        self.root.withdraw()  # 隐藏主窗口

    def tearDown(self):
        self.root.destroy()

    def test_main_window_creation(self):
        """测试主窗口创建"""
        # 导入主应用类
        try:
            from main import AccountingApp
            
            # 创建应用实例（这会测试初始化过程）
            app = AccountingApp(self.root)
            
            # 检查关键组件是否创建
            self.assertIsNotNone(app.notebook)
            self.assertIsNotNone(app.db)
            
            # 检查标签页数量
            tab_count = app.notebook.index("end")
            self.assertGreaterEqual(tab_count, 4)  # 至少应有记账、查看记录、统计分析、预算管理4个标签页
            
        except ImportError:
            self.skipTest("无法导入主应用程序")


class TestFinancialCalculations(unittest.TestCase):
    """测试财务计算逻辑"""

    def setUp(self):
        self.temp_db = tempfile.NamedTemporaryFile(delete=False, suffix='.db')
        self.temp_db.close()
        self.db = Database(self.temp_db.name)

    def tearDown(self):
        os.unlink(self.temp_db.name)

    def test_balance_calculation(self):
        """测试余额计算"""
        # 添加收入和支出
        self.db.add_transaction('income', 1000.0, '工资收入', '工资', '', '')
        self.db.add_transaction('expense', 300.0, '食品酒水', '餐饮', '', '')
        self.db.add_transaction('expense', 200.0, '交通出行', '公共交通', '', '')
        
        # 查询并验证余额
        transactions = self.db.get_transactions()
        total_income = sum(t['amount'] for t in transactions if t['type'] == 'income')
        total_expense = sum(t['amount'] for t in transactions if t['type'] == 'expense')
        balance = total_income - total_expense
        
        self.assertEqual(total_income, 1000.0)
        self.assertEqual(total_expense, 500.0)
        self.assertEqual(balance, 500.0)

    def test_budget_usage_rate(self):
        """测试预算使用率计算"""
        # 设置预算
        budget_amount = 1000.0
        self.db.set_budget('食品酒水', budget_amount, 'monthly')
        
        # 添加支出
        expense_amount = 300.0
        self.db.add_transaction('expense', expense_amount, '食品酒水', '餐饮', '', '')
        
        # 计算使用率
        now = datetime.now()
        status_list = self.db.get_budget_status(now.year, now.month)
        food_status = next(s for s in status_list if s['category'] == '食品酒水')
        
        expected_rate = (expense_amount / budget_amount) * 100
        self.assertAlmostEqual(food_status['usage_rate'], expected_rate, places=1)


def create_test_suite():
    """创建测试套件"""
    loader = unittest.TestLoader()
    suite = unittest.TestSuite()
    
    # 使用TestLoader加载测试用例（替代已弃用的makeSuite）
    suite.addTest(loader.loadTestsFromTestCase(TestDatabase))
    suite.addTest(loader.loadTestsFromTestCase(TestCategories))
    suite.addTest(loader.loadTestsFromTestCase(TestUIComponents))
    suite.addTest(loader.loadTestsFromTestCase(TestFinancialCalculations))
    
    return suite


if __name__ == '__main__':
    # 运行所有测试
    runner = unittest.TextTestRunner(verbosity=2)
    suite = create_test_suite()
    result = runner.run(suite)
    
    # 输出测试结果摘要
    print("\n" + "="*50)
    print("测试结果摘要:")
    print(f"运行测试数: {result.testsRun}")
    print(f"失败数: {len(result.failures)}")
    print(f"错误数: {len(result.errors)}")
    print(f"成功率: {((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100):.1f}%" if result.testsRun > 0 else "N/A")
    print("="*50)